﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;

namespace ShiQuan.HttpServer
{
    /// <summary>
    /// 
    /// </summary>
    public class SocketServer:IDisposable
    {
        /// <summary>
        /// 定义套接字类型
        /// </summary>
        private Socket _socket = null;

        /// <summary>
        /// 监听端口
        /// </summary>
        public int Port { get; set; }

        /// <summary>
        /// 通信使用的编码
        /// </summary>
        public Encoding Encoding { get; set; }

        private bool _runingState = false;

        private Thread _thread = null;

        private ManualResetEvent _manual = new ManualResetEvent(false);

        public SocketServer(int port)
        {
            this.Port = port;
        }

        public void Start()
        {
            //定义侦听端口
            IPEndPoint ipEnd = new IPEndPoint(IPAddress.Any, this.Port);
            this._socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            //连接
            this._socket.Bind(ipEnd);
            //开始侦听
            this._socket.Listen(10);
            this._runingState = true;
            this._thread = new Thread(new ThreadStart(this.Listen));
            this._thread.IsBackground = true;
            this._thread.Start();
        }

        private void Listen()
        {
            while(this._runingState)
            {
                try
                {
                    this._manual.Reset();
                    //一旦接受连接，创建一个客户端
                    //Socket client = this._socket.Accept();
                    this._socket.BeginAccept(new AsyncCallback(AcceptCallback), this._socket);
                }
                finally
                {
                    this._manual.WaitOne();
                }
            }
        }

        void AcceptCallback(IAsyncResult ar)
        {
            this._manual.Set();
            Socket listener = ar.AsyncState as Socket;
            Socket socket = listener.EndAccept(ar);
            try
            {
                this.OnClientConnected(socket);
                //获取客户端的IP和端口
                IPEndPoint ipEndClient = (IPEndPoint)socket.RemoteEndPoint;
                //输出客户端的IP和端口
                Console.Write("Connect with {0} at port {1}", ipEndClient.Address, ipEndClient.Port);
                //定义待发送字符
                string welcome = "Welcome to my server";
                //数据类型转换
                byte[] mBuffer = Encoding.ASCII.GetBytes(welcome);
                //发送
                socket.Send(mBuffer, mBuffer.Length, SocketFlags.None);

                SocketState state = new SocketState();
                state.Socket = socket;
                socket.BeginReceive(state.Buffer, 0, state.Buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), state);
            }
            catch (Exception ex)
            {
                Console.WriteLine("响应异常：" + ex.ToString());
                socket.Close();
            }
        }

        void ReceiveCallback(IAsyncResult ar)
        {
            SocketState state = ar.AsyncState as SocketState;
            try
            {
                Socket socket = state.Socket;
                int recCount = socket.EndReceive(ar);
                if (recCount > 0)
                {
                    this.OnPlaintextReceived(socket, state.Buffer);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("读取信息异常：" + ex.ToString());
            }
            finally
            {
                this.OnClientDisconnected(state.Socket);
                state.Socket.Close();
            }
        }

        #region Events

        /// <summary>
        /// 与客户端的连接已建立事件
        /// </summary>
        public event EventHandler<SocketConnectedEventArgs> ClientConnected;

        private void OnClientConnected(Socket tcpClient)
        {
            if (ClientConnected != null)
            {
                ClientConnected(this, new SocketConnectedEventArgs(tcpClient));
            }
        }

        /// <summary>
        /// 与客户端的连接已断开事件
        /// </summary>
        public event EventHandler<SocketDisconnectedEventArgs> ClientDisconnected;

        private void OnClientDisconnected(Socket tcpClient)
        {
            if (ClientDisconnected != null)
            {
                ClientDisconnected(this, new SocketDisconnectedEventArgs(tcpClient));
            }
        }

        /// <summary>
        /// 接收到数据报文事件
        /// </summary>
        public event EventHandler<SocketReceivedEventArgs<byte[]>> ClientReceived;
        
        private void OnClientReceived(Socket sender, byte[] datagram)
        {
            if (ClientReceived != null)
            {
                ClientReceived(this, new SocketReceivedEventArgs<byte[]>(sender, datagram));
            }
        }
        /// <summary>
        /// 接收到数据报文明文事件
        /// </summary>
        public event EventHandler<SocketReceivedEventArgs<string>> PlaintextReceived;
        private void OnPlaintextReceived(Socket sender, byte[] datagram)
        {
            if (PlaintextReceived != null)
            {
                PlaintextReceived(this, new SocketReceivedEventArgs<string>(
                  sender, this.Encoding.GetString(datagram, 0, datagram.Length)));
            }
        }
        #endregion

        #region IDisposable Support
        private bool disposedValue = false; // 要检测冗余调用

        protected virtual void Dispose(bool disposing)
        {
            if (!disposedValue)
            {
                if (this._socket != null)
                    this._socket.Close();
                this._socket = null;

                if (disposing)
                {
                    // TODO: 释放托管状态(托管对象)。
                }
                // TODO: 释放未托管的资源(未托管的对象)并在以下内容中替代终结器。
                // TODO: 将大型字段设置为 null。
                disposedValue = true;
            }
        }

        // TODO: 仅当以上 Dispose(bool disposing) 拥有用于释放未托管资源的代码时才替代终结器。
        // ~TcpServer() {
        //   // 请勿更改此代码。将清理代码放入以上 Dispose(bool disposing) 中。
        //   Dispose(false);
        // }

        // 添加此代码以正确实现可处置模式。
        public void Dispose()
        {
            // 请勿更改此代码。将清理代码放入以上 Dispose(bool disposing) 中。
            Dispose(true);
            // TODO: 如果在以上内容中替代了终结器，则取消注释以下行。
            // GC.SuppressFinalize(this);
        }
        #endregion
    }
}
